#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/timer.h>


#define TIMER_CNT 1
#define TIMER_NAME "timer"
#define LED_ON 1
#define LED_OFF 0
#define CLOSE_CMD     (_IO(0XEF, 0X1))
#define OPEN_CMD      (_IO(0XEF, 0X2))
#define SETPERIOD_CMD (_IO(0XEF, 0X3))

/* timer设备结构体 */
struct timer_dev{
    dev_t devid;           /* 设备号 */
    int major;     /*  主设备号 */
    int minor;     /* 次设备号 */
    struct cdev cdev;      /* cdev */
    struct class *class;  /* 类 */
    struct device *device;   /* 设备 */
    struct device_node *nd;    /* 设备节点 */
    int led_gpio;
    int timeperiod;
    struct timer_list timer; /* 定义一个定时器 */
    spinlock_t lock; /*定义自旋锁 */
};

struct timer_dev timerdev;



static int led_init(void)
{
    timerdev.nd = of_find_node_by_path("/gpioled");
    if(timerdev.nd == NULL)
        return -EINVAL;
    timerdev.led_gpio = of_get_named_gpio(timerdev.nd,"led-gpio",0);
    if(!gpio_is_valid(timerdev.led_gpio))
        return -EINVAL;
    
    /* 初始化led所使用的IO */
    gpio_request(timerdev.led_gpio,"led");
    gpio_direction_output(timerdev.led_gpio, 1);

    return 0;
}

static int timer_open(struct inode *inode, struct file *filp)
{
    int ret = 0;
    filp->private_data = &timerdev;
    timerdev.timeperiod = 1000;
    ret = led_init();
    if(ret < 0) return ret;
    return 0;
}


void timer_function(unsigned long arg)
{
    struct timer_dev *dev  = (struct timer_dev *)arg;
    static int sta = 1;
    sta = !sta;
    gpio_set_value(dev->led_gpio, sta);
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod));
}


static long  timer_unlocked_ioclt(struct file *filp,unsigned int cmd,unsigned long arg)
{
    struct timer_dev *dev = (struct timer_dev *)filp->private_data;
    unsigned int timerperiod = 0;
    unsigned long flags;
    unsigned int CMD = cmd;
    switch (CMD)
    {
        case CLOSE_CMD:
            del_timer_sync(&dev->timer);
            break;
        case OPEN_CMD:
            mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod));
            break;
        case SETPERIOD_CMD:
            spin_lock_irqsave(&dev->lock, flags);
            dev->timeperiod = arg;
            spin_unlock_irqrestore(&dev->lock,flags);
            mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod));
            break;

        default:
            printk("CMD = %d\r\n", cmd);
            break;
    }
}

static struct file_operations timer_fops = {
    .owner	= THIS_MODULE,
    .open	= timer_open,
    .unlocked_ioctl = timer_unlocked_ioclt,
};

static __init timer_init(void)
{
    /* 初始化自旋锁 */
    spin_lock_init(&timerdev.lock);
    /* 注册字符设备驱动 */
    /* 1、创建设备号 */
    if(timerdev.major){   /* 如果定义了设备号 */
        timerdev.devid = MKDEV(timerdev.major,0);
        register_chrdev_region(timerdev.devid,TIMER_CNT,TIMER_NAME);
    }else {    /* 没有定义设备号 */
        alloc_chrdev_region(&timerdev.devid,0,TIMER_CNT,TIMER_NAME);
        timerdev.major = MAJOR(timerdev.devid);  /* 获取分配的主设备号 */
        timerdev.minor = MINOR(timerdev.devid);  /* 获取分配的次设备号 */
    }

    /* 2、初始化cdev */
    timerdev.cdev.owner = THIS_MODULE;
    cdev_init(&timerdev.cdev,&timer_fops);

    /* 3、添加一个cdev */
    cdev_add(&timerdev.cdev,timerdev.devid,TIMER_CNT);
    /* 4、创建类 */
    timerdev.class = class_create(THIS_MODULE,TIMER_NAME);
    if(IS_ERR(timerdev.class)){
        return PTR_ERR(timerdev.class);
    }

    /* 5、创建设备 */
    timerdev.device = device_create(timerdev.class,NULL,timerdev.devid,NULL,TIMER_NAME);
    if(IS_ERR(timerdev.device)){
        return PTR_ERR(timerdev.device);
    }
    
    /* 6、初始化timer */
    init_timer(&timerdev.timer);
    timerdev.timer.function = timer_function;
    timerdev.timer.data = (unsigned long)&timerdev;
    timerdev.timeperiod = 1000;
    timerdev.timer.expires = jiffies + msecs_to_jiffies(500);
    add_timer(&timerdev.timer);
    return 0;
}

static __exit timer_exit(void)
{
    gpio_free(timerdev.led_gpio);
    del_timer_sync(&timerdev.timer);
    // 注销字符设备驱动
    cdev_del(&timerdev.cdev);
    unregister_chrdev_region(timerdev.devid,TIMER_CNT);
    
    device_destroy(timerdev.class,timerdev.devid);
    class_destroy(timerdev.class);

}

module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhangchaoliang");
